using System;
using System.Threading;
using System.Net.Sockets;
using System.Net;
using SMLib.Automata.Base;
using System.Collections.Generic;

namespace SMLib.Automata.IA
{
	public class SUL_IA_WrapperTcp
	{
		TcpListener iServer, oServer;
		IBlackBox_IA machine;

		public SUL_IA_WrapperTcp (int port, IBlackBox_IA machine)
		{
			IPAddress localAddr = IPAddress.Parse ("127.0.0.1");
			iServer = new TcpListener (localAddr, port);
			oServer = new TcpListener (localAddr, port + 1);
			this.machine = machine;
		}

		/// <summary>
		/// Start this instance.
		/// </summary>
		public void Start ()
		{
			Thread t = new Thread (new ThreadStart (ListenThread));
			t.Start ();
		}

		/// <summary>
		/// Dos the blocking Read.
		/// </summary>
		/// <returns>The blockingread.</returns>
		/// <param name="stream">Stream.</param>
		/// <param name="buff">Buff.</param>
		/// <param name="offset">Offset.</param>
		/// <param name="size">Size.</param>
		/// <param name="timeout">Timeout.</param>
		int DoBlockingread (NetworkStream stream, byte[] buff, int offset, int size, int timeout)
		{
			DateTime startTime = DateTime.Now;
			int totalCount = 0;
			while (true) {
				int count = stream.Read (buff, offset + totalCount, size - totalCount);
				totalCount += count;
				if (totalCount == size)
					return totalCount;

				DateTime currentTime = DateTime.Now;
				TimeSpan sp = currentTime - startTime;
				if (sp.Milliseconds > timeout)
					return totalCount;
				else
					Thread.Sleep (10);				
			}			
		}

		void ListenThread ()
		{	
			//List<ISymbol> obsEvs = new List<ISymbol> ();

			iServer.Start ();
			oServer.Start ();

			TcpClient iClient = iServer.AcceptTcpClient ();            
			TcpClient oClient = oServer.AcceptTcpClient ();            

			Console.WriteLine ("Connected!");
			NetworkStream inputStream = iClient.GetStream ();
			NetworkStream outputStream = oClient.GetStream ();

			byte[] buff = new byte[1000];
			bool endRecieved = false;
			while (!endRecieved) {
				//SMLib.TcpHelper.PACKET p = new TcpHelper.PACKET ();
				Console.Write ("read packet...");
				DoBlockingread (inputStream, buff, 0, 4, 3000);
				Console.WriteLine ("Recieved something!");
				int size = (buff [2] * 256 + buff [3]);

				if (size != 0)
					DoBlockingread (inputStream, buff, 4, size, 3000);
				Console.WriteLine ("{" + buff [0] + " " + buff [1] + " " + buff [2] + " " + buff [3] + "}");

				//* 1 - read the command
				SMLib.TcpHelper.PACKET r = SMLib.TcpHelper.PACKET.FromBytes (buff, size + 4);

				bool evAv = false;
				switch (r.TYPE) {
				//* reset
				case SMLib.TcpHelper.PACKET.TYPE_RESET:
					Console.WriteLine ("Reset");
					//obsEvs.Clear ();
					machine.Reset ();
					byte[] answer = {0,4,0,0};
					inputStream.Write(answer,0,4);
					break;
				//* input					
				case SMLib.TcpHelper.PACKET.TYPE_INPUT:
					Console.WriteLine("Input: "+r.CONTENT);
					SMLib.TcpHelper.PACKET r2=new SMLib.TcpHelper.PACKET();
					if(evAv)
					{
						r2.TYPE=SMLib.TcpHelper.PACKET.TYPE_NOK;
						Console.Write ("$2");
					}
					else
					{
						try
						{
							machine.Perform(new StringSymbol(r.CONTENT));
							r2.TYPE=SMLib.TcpHelper.PACKET.TYPE_OK;
						}catch(NotEnabledActionException)
						{
							r2.TYPE=SMLib.TcpHelper.PACKET.TYPE_NOK;
							Console.Write ("$3");
						}
					}
					byte[] data=r2.ToBytes();
					inputStream.Write(data,0,data.Length);
					break;
				case SMLib.TcpHelper.PACKET.TYPE_LIST_INPUT_REQ:
					//* LIST_INPUT_REQ
					Console.WriteLine("Sigma?");
					string alphabet="";
					foreach(ISymbol s in machine.InputAlphabet.AllSymbols())
					{
						alphabet+=s.ToString()+";";
					}
					SMLib.TcpHelper.PACKET r3=new SMLib.TcpHelper.PACKET();
					r3.TYPE=TcpHelper.PACKET.TYPE_LIST_OF_INPUTS;
					r3.CONTENT=alphabet;
					byte[] data2=r3.ToBytes();
					inputStream.Write(data2,0,data2.Length);
					break;
					case SMLib.TcpHelper.PACKET.TYPE_END:
					endRecieved=true;
					Console.WriteLine("Close packet recieved. Closing...");
					break;
					default:
					Console.WriteLine("Unknown Packet!"+r.TYPE);
					break;
				}

				//* after each action, check the output
				//TODO ofcourse it is not the best way!
				//Thread.Sleep ();
				if (!machine.EventAvailable ()) {
					Console.WriteLine ("--No event!");
				}
				//* 2 - check wheather there is any output!
				else {
					ISymbol o = machine.Event ();
					evAv = true;
					if (r.TYPE != SMLib.TcpHelper.PACKET.TYPE_RESET) {
						Console.WriteLine ("Event " + o.ToString () + " sent!");
						SMLib.TcpHelper.PACKET r2 = new SMLib.TcpHelper.PACKET ();
						r2.TYPE = TcpHelper.PACKET.TYPE_OUTPUT;
						r2.CONTENT = o.ToString ();
						byte[] data=r2.ToBytes();
						outputStream.Write(data,0,data.Length);
					}
				}


			}
			inputStream.Close();
			outputStream.Close();
			iServer.Stop();
			oServer.Stop();
			Console.WriteLine("Done!");
		}
	}
}

